﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using BenevolentSunData;


namespace BenevolentSun
{
    public class LevelScreen : Screen
    {

        #region Gui
        Texture2D iconTray1x1;

        Texture2D prismIconOff;

        Texture2D prismIconOn;

        SpriteFont fontBold, font;

        Texture2D white1x1;
        #endregion


        #region State
        LevelScreenState state;

        bool hasPrismStone = false;

        TimeSpan time = new TimeSpan();

        uint numMoves = 0;

        Level level;

        int levelNum = -1;

        Vector2 cameraVeloc = new Vector2();

        private void startLevel()
        {
            // Load the map and create the level
            Map map = Content.Load<Map>(BenevolentSun.LevelManifest.Levels[levelNum].Source);
            map.LoadContent(Content, GraphicsDevice);
            map = map.Clone() as Map;
            level = new Level(map, Content, GraphicsDevice);

            // Play the level music
            if (level.Map.MusicCueName != null)
            {
                if (level.Map.MusicCueName != AudioManager.CurrentMusic())
                    AudioManager.PlayMusic(level.Map.MusicCueName);
            }
            else
            {
                if (AudioManager.CurrentMusic() != null)
                    AudioManager.PopMusic();
            }

            state = new AnimateIn(this);
        }

        private void nextLevel()
        {
            Session.CompleteLevel(levelNum);
            ScreenManager.AddScreen(new SaveProfileScreen());
            levelNum++;
            if (levelNum == BenevolentSun.LevelManifest.Levels.Count)
            {
                ScreenManager.AddScreen(new GameOverScreen());
                ExitScreen();
            }
            else
            {
                Session.AmbientPower += 15f;
                startLevel();
            }
        }
        #endregion


        #region Initialization
        public LevelScreen(int startingLevel) {
            this.levelNum = startingLevel;
        }

        public override void LoadContent()
        {
            // Load Gui Content
            iconTray1x1 = Content.Load<Texture2D>("Textures/Icon Tray 1x1");
            prismIconOff = Content.Load<Texture2D>("Textures/Prism Icon Off");
            prismIconOn = Content.Load<Texture2D>("Textures/Prism Icon On");
            font = Content.Load<SpriteFont>("Fonts/Ebrima30");
            fontBold = Content.Load<SpriteFont>("Fonts/EbrimaBold50");
            white1x1 = Content.Load<Texture2D>("Textures/White1x1");

            // Start the level
            if (levelNum == -1)
                levelNum = Session.Level;
            startLevel();

            // Set State
            state = new AnimateIn(this);

            base.LoadContent();
        }


        public override void UnloadContent()
        {
            AudioManager.StopMusic();
            base.UnloadContent();
        }
        #endregion


        #region Input
        public override void HandleInput()
        {
            state.HandleInput();
            base.HandleInput();
        }
        #endregion


        #region Updating
        public override void Update(GameTime gameTime, bool isFocusAvailable, bool isOccluded)
        {
            level.Map.Skin.AmbientLightPower = Math.Min(1f, Session.AmbientPower / 7f);

            // Update our level.Camera
            if (isFocusAvailable)
                state.Update(gameTime, isFocusAvailable, isOccluded);

            base.Update(gameTime, isFocusAvailable, isOccluded);
        }
        #endregion


        #region Drawing
        public override void Draw(GameTime gameTime)
        {
            SpriteBatch.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
            state.Draw(gameTime);
            base.Draw(gameTime);
        }
        #endregion
        

        #region States
        private abstract class LevelScreenState
        {

            protected LevelScreen screen;

            public SpriteBatch SpriteBatch { get { return screen.SpriteBatch; } }

            public LevelScreenState(LevelScreen screen)
            {
                this.screen = screen;
            }

            public virtual void HandleInput() { }

            public virtual void Update(GameTime gameTime, bool isFocusAvailable, bool isOccluded) { }

            public virtual void Draw(GameTime gameTime) { }

        }

        private class AnimateIn : LevelScreenState
        {
            SourceCell cell;

            bool levelUpdated = false;

            public AnimateIn(LevelScreen screen)
                : base(screen)
            {
                cell = screen.level.Map.SourceCells[0];
                screen.level.SelectedCell = cell;
                screen.level.ViewCell(cell);

                screen.level.Camera.Eye = new Vector3(screen.level.Camera.Eye.X, -300, screen.level.Camera.Eye.Z);
                screen.level.Camera.At = new Vector3(screen.level.Camera.At.X, screen.level.Camera.Eye.Y, screen.level.Camera.At.Z);

                
            }

            private float trans = 0;

            public override void Update(GameTime gameTime, bool isFocusAvailable, bool isOccluded)
            {
                screen.level.Camera.At += new Vector3(0, (float)gameTime.ElapsedGameTime.TotalSeconds * 200f, 0);
                trans = 0.5f * (1 -(Math.Abs(0 - screen.level.Camera.At.Y) / 300));

                if (screen.level.Camera.At.Y > 0f)
                {
                    screen.level.Camera.At = new Vector3(screen.level.Camera.At.X, 0, screen.level.Camera.At.Z);
                    screen.level.Camera.Eye += new Vector3(0, (float)gameTime.ElapsedGameTime.TotalSeconds * 400f, 0);
                    trans = 0.5f + 0.5f * (1 - (Math.Abs(0 - screen.level.Camera.Eye.Y) / 500));
                }

                if (screen.level.Camera.Eye.Y >= 500f)
                {
                    trans = 1.0f;
                    screen.level.Camera.Eye = new Vector3(screen.level.Camera.Eye.X, 500f, screen.level.Camera.Eye.Z);
                    screen.state = new Playing(screen);
                }

                levelUpdated = true;

                screen.level.Update(gameTime);
            }

            public override void Draw(GameTime gameTime)
            {
                

                if (!levelUpdated)
                {
                    screen.level.Update(gameTime);
                }

                screen.level.Draw(gameTime);

                screen.SpriteBatch.Begin();

                float a = trans;
                

                screen.SpriteBatch.DrawString(
                    screen.fontBold, "Stage: " + (screen.levelNum + 1).ToString(),
                    new Vector2(1280 / 2 - screen.fontBold.MeasureString("Stage: " + (screen.levelNum + 1).ToString()).X / 2, 720 / 2 - screen.fontBold.MeasureString("Stage: " + (screen.levelNum + 1).ToString()).Y / 2),
                    new Color(255,255,255,(byte)(a * 255)));

                screen.SpriteBatch.End();
            }
        }

        private class Playing : LevelScreenState
        {
            Hud hud;

            public Playing(LevelScreen screen)
                : base(screen)
            {
                hud = new Hud(screen.Content);
                hud.StageNumber.Text = Session.Level.ToString();
            }

            public override void HandleInput()
            {
                screen.cameraVeloc = ActionMap.CameraMovement();

                //screen.level.MoveSelector(ActionMap.SelectorMovement());

                if (ActionMap.CellInteraction())
                {
                    if (screen.level.SelectedCell is PrismCell)
                    {
                        if ((screen.hasPrismStone && !((PrismCell)screen.level.SelectedCell).IsActive) || (!screen.hasPrismStone && ((PrismCell)screen.level.SelectedCell).IsActive))
                        {
                            screen.hasPrismStone = !screen.hasPrismStone;
                            if (screen.level.Interact())
                                screen.numMoves++;
                        }
                    }
                    else if (screen.level.Interact())
                        screen.numMoves++;
                }

                if (ActionMap.Pause())
                {
                    PauseScreen pauseScreen = new PauseScreen();
                    pauseScreen.OnExitToMainMenu += new EventHandler(pauseScreen_OnExitToMainMenu);
                    screen.ScreenManager.AddScreen(pauseScreen);
                }
            }

            void pauseScreen_OnExitToMainMenu(object sender, EventArgs e)
            {
                screen.ScreenManager.AddScreen(new MainMenuScreen());
                screen.ExitScreen();
            }

            public override void Update(GameTime gameTime, bool isFocusAvailable, bool isOccluded)
            {
                if (isFocusAvailable && !isOccluded)
                {
                    // Update the hud
                    hud.CellName.Text = screen.level.SelectedCell.Name;
                    hud.CellNote.Text = screen.level.SelectedCell.Note;
                    hud.SetTime(screen.time);
                    hud.Update(gameTime);

                    // TODO: Lose ambient power!
                    Session.AmbientPower -= 1f * (float)gameTime.ElapsedGameTime.TotalSeconds;
                

                    if (screen.cameraVeloc.LengthSquared() != 0)
                    {
                        screen.level.MoveViewConstrained(screen.cameraVeloc * 350f * (float)gameTime.ElapsedGameTime.TotalSeconds);
                        screen.level.SelectCellAt(screen.level.Camera.At);
                    }
                    screen.level.Update(gameTime);

                    screen.time += gameTime.ElapsedGameTime;

                    // Check the winning conditions
                    bool isWin = false;
                    foreach (SinkCell sink in screen.level.Map.SinkCells)
                    {
                        isWin = true;
                        if (sink.IsActivated == false)
                        {
                            isWin = false;
                            break;
                        }
                    }
                    if (isWin)
                    {
                        screen.state = new AnimateOut(screen);
                    }
                }
            }

            
            public override void Draw(GameTime gameTime)
            {
                screen.level.Draw(gameTime);
                
                // begin drawing gui
                SpriteBatch.Begin();

                // draw icon tray and icons
                Vector2 trayPos = new Vector2(25, 720 - 25 - screen.iconTray1x1.Height);
                SpriteBatch.Draw(screen.iconTray1x1, trayPos, Color.White);
                trayPos -= new Vector2(0, 5);
                if (screen.hasPrismStone)
                    SpriteBatch.Draw(screen.prismIconOn, trayPos, Color.White);
                else
                    SpriteBatch.Draw(screen.prismIconOff, trayPos, Color.White);

                int barLength = (int)(400 * Math.Min(1, Session.AmbientPower / 15f));
                SpriteBatch.Draw(screen.white1x1, new Rectangle(15, 100, 45, 400), Color.White);
                if (barLength == 400)
                    SpriteBatch.Draw(screen.white1x1, new Rectangle(15, 100 + (400-barLength), 45, barLength), Color.Yellow);
                else
                    SpriteBatch.Draw(screen.white1x1, new Rectangle(15, 100 + (400 - barLength), 45, barLength), Color.Green);

                hud.Draw(SpriteBatch);

                SpriteBatch.End();
            }
        }

        private class AnimateOut : LevelScreenState
        {
            SinkCell cell;

            public AnimateOut(LevelScreen screen)
                : base(screen)
            {
                cell = screen.level.Map.SinkCells[0];
                screen.level.SelectedCell = cell;
                screen.level.ViewCell(cell);
            }

            private TimeSpan delay = new TimeSpan(0, 0, 0, 0, 400);

            public override void Update(GameTime gameTime, bool isFocusAvailable, bool isOccluded)
            {
                if (delay.Ticks > 0)
                    delay -= gameTime.ElapsedGameTime;
                else
                    screen.level.Camera.At += new Vector3(0, (float)gameTime.ElapsedGameTime.TotalSeconds * 400f, 0);
                
                screen.level.Update(gameTime);

                if (screen.level.Camera.At.Y >= 500f)
                {
                    screen.level.Camera.At = new Vector3(screen.level.Camera.At.X, 500f, screen.level.Camera.At.Z);

                    screen.nextLevel();
                }
            }

            public override void Draw(GameTime gameTime)
            {
                screen.level.Draw(gameTime);
            }
        }
        #endregion

    }

}
